#include <fcntl.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/wait.h>

/*
 * 进程间也可以使用互斥锁，来达到同步的目的。但应在pthread_mutex_init初始化之前，修改其属性为进程间共享。
 *
 * 多线程加锁互斥中的细节处理才是重头戏
 *  细节1： 凡是访问同一个临界资源的线程，都要进行加锁保护，而且必须加同一把锁
 *  细节2： 每一个线程访问临界区资源之前都要加锁，本质上是给临界区加锁
 *  细节3： 线程在访问临界区前，需要先加锁 -> 所有线程都要看到同一把锁 -> 锁本身也是临界资源 -> 锁如何保证自己的安全？
 *          加锁 是为了保护 临界资源 的安全，但 锁 本身也是 临界资源，这就像是一个 先有鸡还是先有蛋的问题，锁 的设计者也考虑到了这个问题，
 *          于是对于 锁 这种 临界资源 进行了特殊化处理：加锁 和 解锁 操作都是原子的，不存在中间状态，也就不需要保护了
 *  细节4： 临界区本身是一行代码，或者一批代码
 *  细节5： 互斥会给其他线程带来影响
 *  细节6： 加锁与解锁配套出现，并且这两个对于锁的操作本身就是原子的
 */

struct mt {
    int num;
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
};

int main(void)
{
    int fd, i;
    struct mt *mm;
    pid_t pid;

    fd = open("mt_test", O_CREAT | O_RDWR, 0777);
    ftruncate(fd, sizeof(*mm));
    mm = mmap(NULL, sizeof(*mm), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    unlink("mt_test");
    //mm = mmap(NULL, sizeof(*mm), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
    memset(mm, 0, sizeof(*mm));

    pthread_mutexattr_init(&mm->mutexattr);                                  // 初始化mutex属性对象
    pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED);    // 修改属性为进程间共享
    pthread_mutex_init(&mm->mutex, &mm->mutexattr);                          // 初始化一把mutex琐

    pid = fork();
    if (pid == 0) {
        for (i = 0; i < 10; i++) {
            pthread_mutex_lock(&mm->mutex);
            (mm->num)++;
            printf("-child----num++   %d\n", mm->num);
            pthread_mutex_unlock(&mm->mutex);
            sleep(1);
        }
    } else if (pid > 0) {
        for ( i = 0; i < 10; i++) {
            sleep(1);
            pthread_mutex_lock(&mm->mutex);
            mm->num += 2;
            printf("-parent---num+=2  %d\n", mm->num);
            pthread_mutex_unlock(&mm->mutex);
        }
        wait(NULL);
    }

    pthread_mutexattr_destroy(&mm->mutexattr);          // 销毁mutex属性对象
    pthread_mutex_destroy(&mm->mutex);					// 销毁mutex
    munmap(mm,sizeof(*mm));								// 释放映射区
    return 0;
}        

/*
	-child----num++   1
	-parent---num+=2  3
	-child----num++   4
	-parent---num+=2  6
	-child----num++   7
	-parent---num+=2  9
	-child----num++   10
	-child----num++   11
	-parent---num+=2  13
	-child----num++   14
	-parent---num+=2  16
	
	xiaozhi@ac:/home/share/work/r/process-communication$ ./p-mutex
	-child----num++   1
	-child----num++   2
	-parent---num+=2  4
	-child----num++   5
	-parent---num+=2  7
	-child----num++   8
	-parent---num+=2  10
	-parent---num+=2  12
	-child----num++   13
	-parent---num+=2  15
	-child----num++   16
	-child----num++   17
	-parent---num+=2  19
	-child----num++   20
	-parent---num+=2  22
*/

